home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 2410 / 2410.xpi / chrome / content / foxmarks-server.js < prev    next >
Text File  |  2010-01-28  |  61KB  |  1,802 lines

  1. /*
  2.  Copyright 2007-2008 Foxmarks Inc.
  3.  
  4.  foxmarks-server.js: component that implements the logical interface to
  5.  the server.
  6.  
  7.  */
  8.  
  9. // TO DO:
  10. // * Deal with failure to retrieve baseline state from server -> merge
  11.  
  12.  
  13.  
  14. function getDatasourceAttribute(synctype, attr){
  15.     switch(synctype){
  16.         case 'bookmarks':
  17.             switch(attr){
  18.                 case 'engine':
  19.                     return BookmarkDatasource.STORAGE_ENGINE;
  20.                 default:
  21.                     throw("getDatasourceAttribute: unknown attribute (" + attr + ")");
  22.             }
  23.             break;
  24.         case 'lastmodifieddate':
  25.             break;
  26.         default:
  27.             throw("getDatasourceAttribute: unknown synctype (" + synctype + ")");
  28.     }
  29.  
  30. }
  31.  
  32. function hasPasswordSync(){
  33.     return "@mozilla.org/login-manager;1" in Cc;
  34.  
  35. }
  36.  
  37. var SYNCSET = {};
  38. if(hasPasswordSync()){
  39.     SYNCSET = {
  40.         "bookmarks": BookmarkDatasource,
  41.         "passwords": PasswordDatasource
  42.     };
  43. }
  44. else {
  45.  
  46.     SYNCSET = {
  47.         "bookmarks": BookmarkDatasource
  48.     };
  49. }
  50.    
  51. function createDatasource(type){
  52.     var model = new SYNCSET[type];
  53.     if(model === undefined)
  54.         throw("createDatasource:  unknown synctype (" + type +")");
  55.  
  56.     return model;
  57. }   
  58.  
  59. function loadDatasourceSet(allItems) {
  60.     var result = [];
  61.     var type;
  62.     var obj = {};
  63.     for(type in SYNCSET){
  64.         if(obj[type] === undefined){
  65.             if(Xmarks.gSettings.isSyncEnabled(type) || allItems){
  66.                 var model = createDatasource(type);
  67.                 result.push(model);
  68.             }
  69.         }
  70.     }
  71.  
  72.     
  73.     return result;
  74. }
  75.  
  76. function SyncServer() {
  77. }
  78.  
  79. SyncServer.prototype = {
  80.     _syncEngine: null,
  81.     _request: null,
  82.     _baselineCache: {},
  83.     cancel: function() {
  84.         if(this._syncEngine) {
  85.             this._syncEngine.cancel();
  86.         } 
  87.         if (this._request){
  88.             this._request.Cancel();
  89.         }
  90.     },
  91.     datacancel: function(){
  92.         if (this._request){
  93.             this._request.Cancel();
  94.         }
  95.     },
  96.     _createSyncEngine: function(datasource){
  97.         var cls = Xmarks.gSettings.useOwnServer ?
  98.             OwnSyncEngine : 
  99.             Syncd2SyncEngine;
  100.         var result = new cls(this,datasource);
  101.  
  102.         result.manual = this.manual;
  103.         return result;
  104.     },
  105.  
  106.     getBaseline: function(syncType,callback){
  107.         var ds = createDatasource(syncType);
  108.         var se = this._createSyncEngine(ds);
  109.         se._fetchBaseline(function(status){
  110.             if(se._responseOK(status, callback)){
  111.                 callback(0, se._baseline);
  112.             }
  113.         });
  114.     },
  115.     suspendWatcher: function(state){
  116.         var os = Cc["@mozilla.org/observer-service;1"]
  117.             .getService(Ci.nsIObserverService);
  118.         os.notifyObservers(null, "foxmarks-watchersuspend", 
  119.             state ? "1" : "0");
  120.     },
  121.  
  122.  
  123.     _doPerDatasource: function(funcname, callback, arg2){
  124.         var list = loadDatasourceSet();
  125.         var that = this;
  126.         var ds = {};
  127.  
  128.         try {
  129.             that.suspendWatcher(true);
  130.         
  131.             // loop through each set of syncdata, using the 
  132.             //  chained callback mechanism, dropping out if
  133.             //  we encounter an error
  134.             var mycallback = function(statuscode){
  135.                 if(statuscode == 0 || statuscode == 444){
  136.                     // 444 -handle purged state
  137.                     if(statuscode == 444){
  138.                         // if we allow purge from other data types
  139.                         // we'll need to modify this string
  140.                         Xmarks.Alert(Xmarks.Bundle().GetStringFromName(
  141.                             "msg.passwordsyncpurged"));
  142.                         Xmarks.gSettings.setSyncEnabled(ds.syncType, false);
  143.                         statuscode = 0;
  144.                     }
  145.                     if(ds && ds.syncType !== undefined){
  146.                         Xmarks.gSettings.SyncComplete(ds.syncType);
  147.                         Xmarks.SetProgressComponentStatus(ds.syncType, "end");
  148.                     }
  149.                     ds = list.shift();
  150.                     if(ds){
  151.                         that._syncEngine = that._createSyncEngine(ds);
  152.                         Xmarks.SetProgressComponentStatus(ds.syncType, "start");
  153.                         if(arg2 !== undefined){
  154.                             that._syncEngine[funcname](arg2, mycallback);
  155.                         }
  156.                         else {
  157.                             that._syncEngine[funcname](mycallback);
  158.                         }
  159.                     }
  160.                     else {
  161.                         callback(statuscode);
  162.                         that.suspendWatcher(false);
  163.                         that._syncEngine = null;
  164.                         if(!Xmarks.gSettings.useBaselineCache){
  165.                             that._baselineCache = {};
  166.                         }
  167.                     }
  168.                 }
  169.                 else {
  170.                     callback(statuscode);
  171.                     that.suspendWatcher(false);
  172.                     that._syncEngine = null;
  173.                     if(!Xmarks.gSettings.useBaselineCache){
  174.                         that._baselineCache = {};
  175.                     }
  176.                 }
  177.             }
  178.  
  179.             mycallback(0);
  180.         }catch(e){
  181.             that.suspendWatcher(false);
  182.             if(typeof e == "object" && e.message){
  183.                 Xmarks.LogWrite("Sync Error: " + e.message + "(" +
  184.                         e.fileName + ": " + e.lineNumber + ")");
  185.             } else {
  186.                 Xmarks.LogWrite("Synchronization failed. Error is " + 
  187.                     e.toSource() + " (type = " + typeof e + ")");
  188.             }
  189.             if(typeof e == "number"){
  190.                 mycallback(e);
  191.             } else {
  192.                 mycallback(4);
  193.             }
  194.         }
  195.     },
  196.     sync: function(prevState, callback){
  197.         Xmarks.SetProgressMessage("progress.syncing");
  198.         this._doPerDatasource("sync", callback, prevState);
  199.     },
  200.     upload: function(callback){
  201.         Xmarks.SetProgressMessage("progress.syncing");
  202.         this._doPerDatasource("upload", callback);
  203.     },
  204.     download: function(callback){
  205.         Xmarks.SetProgressMessage("progress.syncing");
  206.         this._doPerDatasource("download", callback);
  207.     },
  208.     merge: function(local, callback){
  209.         Xmarks.SetProgressMessage("progress.syncing");
  210.         this._doPerDatasource("merge", callback, local);
  211.     },
  212.     status: function(syncType, callback){
  213.         var ds = createDatasource(syncType);
  214.         var se = this._createSyncEngine(ds);
  215.         se.status(callback);
  216.     },
  217.     extstatus: function(syncType, callback){
  218.         var ds = createDatasource(syncType);
  219.         var se = this._createSyncEngine(ds);
  220.         se.extstatus(callback);
  221.     },
  222.     getProfileNames: function(callback){
  223.         var se = this._createSyncEngine(null);
  224.         se.getProfileNames(callback);
  225.     },
  226.     _getCorrelatorInfo: function(url, corruri, callback, flags){
  227.         const https = "https://";
  228.         flags = flags || {};
  229.         var str;
  230.         var self = this;
  231.         var protocol = url.substr(0,https.length).toLowerCase();
  232.         var funcFinished = function(status, response) {
  233.             self._request = null;
  234.             callback(status, response);
  235.         };
  236.  
  237.         // we contact correlator via https if the url we are interested
  238.         // in is https
  239.         if (Xmarks.gSettings.securityLevel == 1 || protocol == https ){ 
  240.             str = https;
  241.         } else {
  242.             str = "http://";
  243.         }
  244.  
  245.         str += Xmarks.gSettings.apiHost + corruri;
  246.         this._request = new Request(
  247.             "POST",
  248.             str,
  249.             {
  250.                 urls: [url],
  251.                 mid: Xmarks.gSettings.machineId,
  252.                 apihost: Xmarks.gSettings.apiHost,
  253.                 drifthost: Xmarks.gSettings.driftHost,
  254.                 statichost: Xmarks.gSettings.staticHost,
  255.                 cid: "xmfx"
  256.             }, 
  257.             {
  258.                 isAuthRequest: false,
  259.                 headers: null,
  260.                 ignoreBody: false,
  261.                 noJSON: flags.noJSON,
  262.                 noAuthDialog: true
  263.             }
  264.         );
  265.         this._request.Start(funcFinished);
  266.     },
  267.     getSimilarSites: function(url, callback){
  268.         return this._getCorrelatorInfo(
  269.             url, 
  270.             "/internal/bib", 
  271.             callback, 
  272.             {
  273.                 noJSON: true
  274.             }
  275.         );
  276.     },
  277.     getTurboTags: function(url, callback){
  278.         return this._getCorrelatorInfo(url, "/internal/topics_review/read", 
  279.             callback);
  280.     },
  281.  
  282.     updateReview: function(url, url_id, rating, review, callback) {
  283.         var useSSL = (Xmarks.gSettings.securityLevel == 1 || 
  284.             url.indexOf("https://") == 0);
  285.         this._request = new Request("POST", 
  286.             { protocol: useSSL ? "https" : "http", 
  287.                 host: Xmarks.gSettings.apiHost, 
  288.                 path: "/internal/review/write" },
  289.             { url_id: url_id, rating: rating, review: review });
  290.         this._request.Start(callback);
  291.     },
  292.  
  293.     purgepasswords: function(callback){
  294.         var ds = createDatasource("passwords");
  295.         var se = this._createSyncEngine(ds);
  296.         se.purgepasswords(callback);
  297.     },
  298.     restore: function(rev,callback){
  299.         var ds = createDatasource("bookmarks");
  300.         var se = this._createSyncEngine(ds);
  301.         try {
  302.             se.restore(rev,callback);
  303.         } catch(e){
  304.             Xmarks.LogWrite("Restore Failed (" + e.message + ")");
  305.             if(typeof e == "number"){
  306.                 callback(e);
  307.             } else {
  308.                 callback(4);
  309.             }
  310.         }
  311.     },
  312.     getrevision: function(rev,callback){
  313.         var ds = createDatasource("bookmarks");
  314.         var se = this._createSyncEngine(ds);
  315.         try {
  316.             se.getrevision(rev,callback);
  317.         } catch(e){
  318.             Xmarks.LogWrite("GetRevision Failed (" + e.message + ")");
  319.             if(typeof e == "number"){
  320.                 callback(e);
  321.             } else {
  322.                 callback(4);
  323.             }
  324.         }
  325.     },
  326.     getrevisions: function(callback){
  327.         var ds = createDatasource("bookmarks");
  328.         var se = this._createSyncEngine(ds);
  329.         try {
  330.             se.getrevisions(callback);
  331.         } catch(e){
  332.             Xmarks.LogWrite("GetRevision Failed (" + e.message + ")");
  333.             if(typeof e == "number"){
  334.                 callback(e);
  335.             } else {
  336.                 callback(4);
  337.             }
  338.         }
  339.     },
  340.     verifypin: function(pin, callback){
  341.         var ds = createDatasource("passwords");
  342.         var se = this._createSyncEngine(ds);
  343.         try {
  344.             se.verifypin(pin, callback);
  345.         } catch(e){
  346.             Xmarks.LogWrite("Verify Pin Failed (" + e.message + ")");
  347.             if(typeof e == "number"){
  348.                 callback(e);
  349.             } else {
  350.                 callback(4);
  351.             }
  352.         }
  353.     },
  354.     runUnitTest: function(){
  355.         gFoxmarksUT.run();
  356.     },
  357.     countItems: function(synctype, itemtype, callback){
  358.         var ds = createDatasource(synctype);
  359.         var se = this._createSyncEngine(ds);
  360.         se.countItems(itemtype, callback);
  361.     }
  362. };
  363.  
  364.  
  365. function SyncEngine(datasource) {
  366.     this._datasource = datasource;
  367. }
  368.  
  369. SyncEngine.prototype = {
  370.     request: null,
  371.     _iscancelled: false,
  372.  
  373.     get _baseline() {
  374.         return this._mgr._baselineCache[
  375.             this._datasource.syncType
  376.         ];
  377.     },
  378.     set _baseline(val) {
  379.         this._mgr._baselineCache[
  380.             this._datasource.syncType
  381.         ] = val;
  382.     },
  383.  
  384.     cancel:  function() {
  385.         if (this.request) {
  386.             this.request.Cancel();
  387.         }
  388.         this._iscancelled = true;
  389.     },
  390.     isCancelled: function(){
  391.         return this._iscancelled;
  392.     },
  393.  
  394.     _responseOK: function(response, callback) {
  395.         if(this.isCancelled()){
  396.             callback(2);
  397.             return false;
  398.         }
  399.         else if (typeof response == 'number') {
  400.             if (!response) {
  401.                 return true;
  402.             } else {
  403.                 callback(response);
  404.             }
  405.         } else if (typeof response == 'object') {
  406.             if (!response.status) {
  407.                 return true;
  408.             } else {
  409.                 if (response.status == 403) {
  410.                     Xmarks.LogWrite("Got a 403");
  411.                     Handle403(response);
  412.                 } else if (response.status == 503) {
  413.                     try {
  414.                         gServerBackoff[this._datasource.syncType] = response.backoff_delay || 0;
  415.                         if (gServerBackoff[this._datasource.syncType]) {
  416.                             Xmarks.LogWrite("Server wants us to wait " +
  417.                                 gServerBackoff[this._datasource.syncType] + " seconds");
  418.                         }
  419.                     } catch(e) {}
  420.                 }
  421.                 callback(response.status);
  422.                 return false;
  423.             }
  424.         } else {
  425.             throw Error("unexpected response type: " + response);
  426.         }
  427.     },
  428.     countItems: function(itemtype, callback){
  429.         var self = this;
  430.         var ns = new Nodeset(self._datasource);
  431.         ns.FetchFromNative(function(status){
  432.             if (self._responseOK(status, callback)) {
  433.                 var ctr = 0;
  434.                 ns.OnTree(
  435.                     function(nid){
  436.                       if(ns.Node(nid).ntype == itemtype)
  437.                         ctr++;
  438.                     },
  439.                     function(){
  440.                         callback(status, ctr);
  441.                     }
  442.                 );
  443.             }
  444.         });
  445.     },
  446.     sync: function(prevState, callback) {
  447.         // Local Vars
  448.         var lns = null, sns = null;
  449.         var lcs = null, scs = null;
  450.         var sco = null;
  451.         var lastModified = 0;
  452.         var self = this;
  453.         var fms = Cc["@foxcloud.com/extensions/foxmarks;1"].
  454.             getService(Ci.nsIFoxmarksService);
  455.         self.startTime = Xmarks.LogTimer("Timer Sync Start");
  456.         var funcCheckStatus = function(status, response) {
  457.             if (self._responseOK(status, callback)) {
  458.                 if (response.isreset == true) {
  459.                     self.upload(callback);
  460.                 } else {
  461.                     self.merge(true, callback);
  462.                 }
  463.             }
  464.         };
  465.  
  466.         // Local functions
  467.         var funcGetServerChanges = function(status) {
  468.             if (self._responseOK(status, callback)) {
  469.                 Xmarks.LogTimer("Timer Sync: Fetch Baseline", self.startTime);
  470.                 // Get changes.
  471.                 self._getServerChanges(funcGotServerChanges);
  472.             }
  473.         };
  474.         var funcGotServerChanges = function(status, serverContextObject) {
  475.             if (self._responseOK(status, callback)) {
  476.                 Xmarks.LogTimer("Timer Sync: Got Server Changes", self.startTime);
  477.                 sco = serverContextObject;
  478.                 if (!sco.continuous) {
  479.                     // Someone has done an upload. Allow the user the option
  480.                     // of performing a merge or a download.
  481.  
  482.                     switch (self._datasource.DiscontinuityPrompt()) {
  483.                     case 0: // merge
  484.                         return self.merge(false, callback);
  485.  
  486.                     case 1: // download
  487.                         return self.download(callback);
  488.  
  489.                     case 2: // cancel
  490.                         callback(2);
  491.                         return;
  492.                     }
  493.                 }
  494.                 if (!sco.mscs.length && prevState == 'ready' && 
  495.                         self._haveFetched) {
  496.                     Xmarks.LogWrite("Nothing to see here; move along.");
  497.                     callback(0);
  498.                     return;
  499.                 }
  500.  
  501.                 //Xmarks.SetProgressMessage("progress.syncing");
  502.                 scs = sco.mscs;
  503.                 sns = sco.sns;
  504.  
  505.                 // Calculate minimal local change set.
  506.                 lastModified = fms.getLastModified(self._datasource.synctype);
  507.                 lns = new Nodeset(self._datasource);
  508.                 lns.FetchFromNative(funcGotLocalNodeset);
  509.                 self._haveFetched = true;
  510.             }
  511.         }
  512.         var funcGotLocalNodeset = function(status) {
  513.             if (self._responseOK(status, callback)) {
  514.                 Xmarks.LogTimer("Timer Sync: Fetched Local", self.startTime);
  515.                 var base = new Nodeset(self._datasource,self._baseline);
  516.                 base.Compare(lns, funcGetLocalCommandset);
  517.             }
  518.         }
  519.         var funcGetLocalCommandset = function(status, lcs) {
  520.             if (self._responseOK(status, callback)) {
  521.                 Xmarks.LogTimer("Timer Sync: Command Set", self.startTime);
  522.                 // Check to see whether there's been a clobber
  523.                 if (lns.length < 3 * self._baseline.length / 4) {
  524.                     Xmarks.LogWrite("Yikes! Length was " + self._baseline.length + 
  525.                         " but is now " + lns.length);
  526.                     self.manual = true;
  527.                     if (!self._datasource.ClobberDialog(lns.length, self._baseline.length)) {
  528.                         // User canceled. Back off for 24 hours.
  529.                         gBackoffUntil = Date.now() + 24 * 60 * 60 * 1000;
  530.                         callback(2);
  531.                         return;
  532.                     }
  533.                 }
  534.             
  535.                 Xmarks.LogWrite("lcs = " + lcs.length + " scs = " + scs.length);
  536.  
  537.                 try {
  538.                     Synchronize(self._baseline, lcs, scs, lns, sns, 
  539.                         funcSyncComplete);
  540.                 } catch (e) {
  541.                     Xmarks.LogWrite("Synchronization failed. Error is " + 
  542.                         e.toSource());
  543.                     if(typeof e == "number"){
  544.                         callback(e);
  545.                     } else {
  546.                         callback(4);
  547.                     }
  548.                 }
  549.             }
  550.         };
  551.         var funcSyncComplete = function(finalLcs,
  552.                 finalScs,
  553.                 conflicts,
  554.                 showedUI) {
  555.                 Xmarks.LogTimer("Timer Sync: Sync Set", self.startTime);
  556.             //Xmarks.SetProgressMessage("progress.writing");
  557.  
  558.             if (showedUI) {
  559.                 self.manual = true;
  560.             }
  561.  
  562.             if (lastModified != fms.getLastModified(self._datasource.synctype)) {
  563.                 Xmarks.LogWrite("Local datastore changed during sync");
  564.                 Xmarks.LogWrite("lastModified " + lastModified + " != " + fms.getLastModified(self._datasource.synctype));
  565.                 callback(409);
  566.                 return;
  567.             }
  568.  
  569.             // Scoping: assign back to our enclosing function's locals
  570.             lcs = finalLcs;
  571.             scs = finalScs;
  572.  
  573.             // Apply the server's changes to the local set.
  574.             try {
  575.                 lns = new Nodeset(self._datasource, lns);
  576.                 scs.execute(lns);
  577.                 Xmarks.LogTimer("Timer Sync: Applied Changes", self.startTime);
  578.             } catch (e) {
  579.                 Xmarks.LogWrite("execute failed: " + e.toSource());
  580.                 if(typeof e == "number"){
  581.                     callback(e);
  582.                 } else {
  583.                     callback(4);
  584.                 }
  585.                 return;
  586.             }
  587.             
  588.             // Write local changes (if any) to the server.
  589.             if (lcs.set.length > 0) {
  590.                 self._writeServerChanges(lcs, lns, sco, conflicts, 
  591.                     funcWroteServerChanges);
  592.             } else {
  593.                 funcWroteServerChanges();
  594.             }
  595.         };
  596.         var funcWroteServerChanges = function(obj) {
  597.             if (obj) {      // We had something to write
  598.                 if (self._responseOK(obj, callback)) {
  599.                 } else {
  600.                     Xmarks.LogWrite("putchanges failed; response is " + 
  601.                             obj.toJSONString());
  602.                     return;
  603.                 }
  604.             }
  605.  
  606.             Xmarks.LogTimer("Timer Sync: Sent Server Changes", self.startTime);
  607.             // Flush the local nodeset back to the native datastore if it changed.
  608.             if (scs.set.length > 0) {
  609.                 //Xmarks.SetProgressMessage("progress.loading");
  610.                 lns.FlushToNative(funcWroteLocalChanges);
  611.             } else {
  612.                 funcWroteLocalChanges(0);
  613.             }
  614.         };
  615.         var funcWroteLocalChanges = function(response) {
  616.             // If we got changes from the server or wrote changes to
  617.             // the server, we'll have a new revision number. If neither
  618.             // of these happened, then the sync was a big no-op.
  619.             if (self._responseOK(response, callback)) {
  620.                 Xmarks.LogTimer("Timer Sync: Wrote to Native", self.startTime);
  621.                 if (lcs.set.length > 0 || scs.set.length > 0) {
  622.                     self._completeTransaction(lns, sco, callback);
  623.                 } else {
  624.                     // We didn't do anything, but note that we successfully
  625.                     // Synced nonetheless.
  626.                     callback(0);         // Done!
  627.                 }
  628.             }
  629.         };
  630.  
  631.         // function start
  632.  
  633.         // If we've never synced, do an upload or merge.
  634.         if (!Xmarks.gSettings.getHaveSynced(self._datasource.syncType)) {
  635.             self.status(funcCheckStatus);
  636.         }
  637.         else {
  638.             if(Xmarks.gSettings.mustUpload(self._datasource.syncType)){
  639.                 Xmarks.LogWrite("Forced Upload: Pin Reset");
  640.                 self.upload(function(response){
  641.                     if(self._responseOK(response, callback)){
  642.                         Xmarks.gSettings.setMustUpload(self._datasource.syncType, false);
  643.                         callback(response);
  644.                     }
  645.                 });
  646.                 return;
  647.  
  648.             }
  649.             else if(Xmarks.gSettings.mustMerge(self._datasource.syncType)){
  650.                 Xmarks.LogWrite("Forced Merge for Passwords");
  651.                 self.merge(true, function(response){
  652.                     if(self._responseOK(response, callback)){
  653.                         Xmarks.gSettings.setMustMerge(self._datasource.syncType, false);
  654.                         callback(response);
  655.                     }
  656.                 });
  657.                 return;
  658.  
  659.             }
  660.             // Normal sync: make sure we've got a baseline to work with.
  661.             //Xmarks.SetProgressMessage("progress.downloading");
  662.             self._fetchBaseline(funcGetServerChanges);
  663.             return;
  664.         }
  665.     }
  666. };
  667.  
  668.  
  669. function Syncd2SyncEngine(mgr, datasource) {
  670.     this._datasource = datasource;
  671.     this._mgr = mgr;
  672. }
  673. Syncd2SyncEngine.prototype = new SyncEngine;
  674.  
  675. function OwnSyncEngine(mgr, datasource) {
  676.     this._datasource = datasource;
  677.     this._mgr = mgr;
  678. }
  679. OwnSyncEngine.prototype = new SyncEngine;
  680.  
  681. Syncd2SyncEngine.prototype._args = function(dict, ignoreUpload) {
  682.     if (this.manual) {
  683.         dict["manual"] = true;
  684.     }
  685.     if (gFailureCount) {
  686.         dict["retry"] = gFailureCount;
  687.     }
  688.     if (Xmarks.gSettings.machineId) {
  689.         if (!dict["log"]) {
  690.             dict["log"] = {};
  691.         }
  692.         dict["log"]["mid"] = Xmarks.gSettings.machineId;
  693.         dict["log"]["serp"] = Xmarks.gSettings.serpEnabled ? Xmarks.gSettings.serpMaxItems : 0;
  694.         dict["log"]["ssEnabled"] = Xmarks.gSettings.simsiteEnabled;
  695.  
  696.         var st = [];
  697.         for(var x = 0; x < 10; x++){
  698.             st.push(Xmarks.gSettings.getST(false,x));
  699.         }
  700.  
  701.         if(st.toSource() != "[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}]"){
  702.             dict["log"]["st"] = st;
  703.         }
  704.         st = [];
  705.         for(var x = 0; x < 10; x++){
  706.             st.push(Xmarks.gSettings.getST(true,x));
  707.             Xmarks.gSettings.clearST(x);
  708.         }
  709.  
  710.         if(st.toSource() != "[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}]"){
  711.             dict["log"]["ust"] = st;
  712.         }
  713.         if(Xmarks.gSettings.trSERP){
  714.             dict["log"]["trs"] = Xmarks.gSettings.trSERP;
  715.             Xmarks.gSettings.trSERP = "";
  716.         }
  717.         if(Xmarks.gSettings.numTurboTags){
  718.             dict["log"]["ttags"] = Xmarks.gSettings.numTurboTags;
  719.             Xmarks.gSettings.numTurboTags = 0;;
  720.         }
  721.     }
  722.     if (Xmarks.gSettings.viewId) {
  723.         if(!ignoreUpload){
  724.             dict.view = Xmarks.gSettings.viewId;
  725.         }
  726.     }
  727.  
  728.  
  729.     return dict;
  730. }
  731.  
  732. Syncd2SyncEngine.prototype.upload = function(callback) {
  733.     var self = this;
  734.     var ns = new Nodeset(this._datasource);
  735.  
  736.     // if we are doing a force upload, then that trumps forcedMerge
  737.     if(Xmarks.gSettings.mustUpload(self._datasource.syncType)){
  738.         Xmarks.gSettings.setMustMerge(self._datasource.syncType, false);
  739.     }
  740.     // even though the user says upload, we don't want
  741.     // to do that for passwords during initial sync
  742.     if(Xmarks.gSettings.mustMerge(self._datasource.syncType)){
  743.         Xmarks.LogWrite("Forced Merge for Passwords");
  744.         self.merge(true, function(response){
  745.             if(self._responseOK(response, callback)){
  746.                 Xmarks.gSettings.setMustMerge(self._datasource.syncType, false);
  747.                 callback(response);
  748.             }
  749.         });
  750.         return;
  751.     }
  752.  
  753.     // Hack: we don't seem to be able to handle
  754.     // authentication during an upload request.
  755.     // So to avoid trouble, we do a throw-away
  756.     // status request first. If auth is required,
  757.     // it will be handled during the status request,
  758.     // after which we will get on to our real work.
  759.  
  760.     var funcForceAuth = function(status) {
  761.         if (self._responseOK(status, callback)) {
  762.             //Xmarks.SetProgressMessage("progress.writing");
  763.             ns.FetchFromNative(funcFetched);
  764.         }
  765.     };
  766.     var funcFetched = function(e) {
  767.         if (e) {
  768.             callback(e);
  769.             return;
  770.         }
  771.         ns.ProvideCommandset(funcContinue);
  772.     };
  773.     var funcContinue = function(status, cs) {
  774.         if (self._responseOK(status, callback)) {
  775.             self.request = new Request("POST", 
  776.                 {
  777.                     path: "/sync/" + self._datasource.syncType + "/upload",
  778.                     host: Xmarks.gSettings.getServerHost(self._datasource.syncType)
  779.                 }, 
  780.                 self._args({ "commands" : cs },
  781.                     Xmarks.gSettings.mustUpload(self._datasource.syncType)
  782.                 )
  783.             );
  784.             self.request.Start(funcDone);
  785.         }
  786.     };
  787.     var funcDone = function(response) {
  788.         if (self._responseOK(response, callback)) {
  789.             if (!response.toprev) {
  790.                 Xmarks.LogWrite("Error: Invalid response to upload");
  791.                 callback(500);
  792.             } else {
  793.                 Xmarks.gSettings.setMustUpload(self._datasource.syncType, false);
  794.                 self._completeTransaction(ns, { toprev: response.toprev }, 
  795.                     callback);
  796.             }
  797.         }
  798.     };
  799.  
  800.     if (Xmarks.gSettings.viewId && !Xmarks.gSettings.mustUpload(self._datasource.syncType)) {
  801.         this._uploadWithProfile(callback);
  802.         return;
  803.     }
  804.     self.status(funcForceAuth);
  805. }
  806.  
  807. Syncd2SyncEngine.prototype.purgepasswords = function(callback) {
  808.     var self = this;
  809.     var ns = new Nodeset(this._datasource);
  810.  
  811.     // Hack: we don't seem to be able to handle
  812.     // authentication during an upload request.
  813.     // So to avoid trouble, we do a throw-away
  814.     // status request first. If auth is required,
  815.     // it will be handled during the status request,
  816.     // after which we will get on to our real work.
  817.  
  818.     var funcForceAuth = function(status) {
  819.         if (self._responseOK(status, callback)) {
  820.             self.request = new Request("POST", 
  821.                 {
  822.                     path: "/sync/" + self._datasource.syncType + "/purge",
  823.                     host: Xmarks.gSettings.getServerHost(self._datasource.syncType)
  824.                 } , self._args({ mode: "data"}));
  825.             self.request.Start(function(statuspurge){
  826.                 if (self._responseOK(statuspurge, callback)) {
  827.                     Xmarks.gSettings.SetSyncRevision(
  828.                         self._datasource.syncType, 0);
  829.                     callback(0);
  830.                 }
  831.            });
  832.         }
  833.     };
  834.     self.status(funcForceAuth);
  835. }
  836. Syncd2SyncEngine.prototype._uploadWithProfile = function(callback) {
  837.     var self = this;
  838.     var revision;
  839.     var ns;
  840.     var lns;
  841.  
  842.     var funcDone = function(response) {
  843.         if (self._responseOK(response, callback)) {
  844.             if (!response.toprev || !response.commands) {
  845.                 Xmarks.LogWrite("Error: Invalid response to download");
  846.                 callback(500);
  847.                 return;
  848.             }
  849.             revision = response.toprev;
  850.             ns  = new Nodeset(self._datasource);
  851.             var cs = new Commandset(response.commands);
  852.             try {
  853.                 cs.execute(ns);
  854.             } catch (e) {
  855.                 Xmarks.LogWrite("execute failed: " + e.toSource());
  856.                 if(typeof e == "number"){
  857.                     callback(e);
  858.                 } else {
  859.                     callback(4);
  860.                 }
  861.                 return;
  862.             }
  863.  
  864.             // Retrieve local set.
  865.             lns = new Nodeset(self._datasource);
  866.             lns.FetchFromNative(funcGotLocal);
  867.         }
  868.     };
  869.     var funcGotLocal = function(response) {
  870.         // Calculate difference: server -> local.
  871.         if (self._responseOK(response, callback)) {
  872.             ns.Compare(lns, funcCompared);
  873.         }
  874.     };
  875.     var funcCompared = function(status, cs) {
  876.         if (self._responseOK(status, callback)) {
  877.             if (cs.length) {
  878.                 //Xmarks.SetProgressMessage("progress.uploading");
  879.                 self.request = new Request("POST",
  880.                     {
  881.                         path: "/sync/" + self._datasource.syncType + "/putchanges",
  882.                         host: Xmarks.gSettings.getServerHost(self._datasource.syncType)
  883.                     }, 
  884.                     self._args({ "baserev": revision, "commands" : cs }));
  885.                 self.request.Start(funcFinished);
  886.             } else {
  887.                 funcFinished({ status: 0, toprev: revision});
  888.             }
  889.         }
  890.     };
  891.     var funcFinished = function(response) {
  892.         if (self._responseOK(response, callback)) {
  893.             if (!response.toprev) {
  894.                 Xmarks.LogWrite("Error: Invalid response to putchanges");
  895.                 callback(500);
  896.             } else {
  897.                 self._completeTransaction(ns, { toprev: response.toprev }, 
  898.                     callback);
  899.             }
  900.         }
  901.     };
  902.  
  903.  
  904.     // Download current server state.
  905.     Xmarks.SetProgressMessage("progress.downloading");
  906.     self.request = new Request("POST",
  907.         {
  908.             path: "/sync/"  + self._datasource.syncType + "/download",
  909.             host: Xmarks.gSettings.getServerHost(self._datasource.syncType)
  910.         }, self._args({}));
  911.     self.request.Start(funcDone);
  912. }
  913.  
  914. Syncd2SyncEngine.prototype.download = function(callback, rev) {
  915.     var self = this;
  916.     var ns;
  917.     var revision;
  918.  
  919.     // resetPIN trumps download (only occurs in the case of
  920.     // resetPIN in setup dialog)
  921.     if(Xmarks.gSettings.mustUpload(self._datasource.syncType)){
  922.         Xmarks.LogWrite("Forced Upload: Pin Reset");
  923.         self.upload(function(response){
  924.             if(self._responseOK(response, callback)){
  925.                 Xmarks.gSettings.setMustUpload(self._datasource.syncType, false);
  926.                 callback(response);
  927.             }
  928.         });
  929.         return;
  930.     }
  931.     else if(Xmarks.gSettings.mustMerge(self._datasource.syncType)){
  932.         Xmarks.LogWrite("Forced Merge for Passwords");
  933.         self.merge(true, function(response){
  934.             if(self._responseOK(response, callback)){
  935.                 Xmarks.gSettings.setMustMerge(self._datasource.syncType, false);
  936.                 callback(response);
  937.             }
  938.         });
  939.         return;
  940.     }
  941.     var funcDone = function(response) {
  942.         if (self._responseOK(response, callback)) {
  943.             if (!response.toprev || !response.commands) {
  944.                 Xmarks.LogWrite("Error: Invalid response to download");
  945.                 callback(500);
  946.                 return;
  947.             }
  948.             revision = response.toprev;
  949.             ns = new Nodeset(self._datasource);
  950.             var cs = new Commandset(response.commands);
  951.             try {
  952.                 cs.execute(ns);
  953.             } catch (e) {
  954.                 Xmarks.LogWrite("execute failed: " + e.toSource());
  955.                 if(typeof e == "number"){
  956.                     callback(e);
  957.                 } else {
  958.                     callback(4);
  959.                 }
  960.                 return;
  961.             }
  962.             //Xmarks.SetProgressMessage("progress.loading");
  963.             ns.FlushToNative(funcFinished);
  964.         }
  965.     };
  966.     var funcFinished = function(response) {
  967.         if (self._responseOK(response, callback)) {
  968.             if(!rev){
  969.                 self._completeTransaction(ns, { toprev: revision }, callback);
  970.             } else {
  971.                 callback(response);
  972.             }
  973.         }
  974.     };
  975.  
  976.     //Xmarks.SetProgressMessage("progress.downloading");
  977.     var params = {};
  978.     if(rev){
  979.         params.rev = rev;
  980.     }
  981.     self.request = new Request("POST", 
  982.         {
  983.             path: "/sync/" + self._datasource.syncType + "/download",
  984.             host: Xmarks.gSettings.getServerHost(self._datasource.syncType)
  985.         }, self._args(params));
  986.     self.request.Start(funcDone);
  987. }
  988.  
  989.  
  990. Syncd2SyncEngine.prototype.verifypinbrokenroot = function(pin, callback) {
  991.     var self = this;
  992.     var ns;
  993.     var revision;
  994.  
  995.     var funcDone = function(response) {
  996.         if (self._responseOK(response, callback)) {
  997.             for(var nid in response.nodes){
  998.                 if(!response.nodes.hasOwnProperty(nid)){
  999.                     continue;
  1000.                 } else if(response.nodes[nid].data){
  1001.                     var result = self._datasource.verifyPin(pin, response.nodes[nid]); 
  1002.                     callback(result ? 0 : 100);
  1003.                     return;
  1004.                 }
  1005.             }
  1006.             // must have been no nodes with data, so assume it's valide
  1007.             callback(0);
  1008.         }
  1009.     };
  1010.  
  1011.     //Xmarks.SetProgressMessage("progress.verifying");
  1012.     Xmarks.LogWrite("Verifying PIN Broken Root");
  1013.     self.request = new Request("POST", 
  1014.         {
  1015.             path: "/sync/" + self._datasource.syncType + "/state",
  1016.             host: Xmarks.gSettings.getServerHost(self._datasource.syncType)
  1017.         }, {"nodes":"ROOT", "depth":"children"});
  1018.     self.request.Start(funcDone);
  1019. }
  1020.  
  1021. Syncd2SyncEngine.prototype.restore  = function(rev,callback) {
  1022.     var self = this;
  1023.  
  1024.     this.download(
  1025.         function(response){
  1026.             if (self._responseOK(response, callback)) {
  1027.                // self.upload(callback);
  1028.                self.sync('dirty',
  1029.                    function(response){
  1030.                         if (self._responseOK(response, callback)) {
  1031.                             Xmarks.gSettings.SyncComplete(
  1032.                                 self._datasource.syncType
  1033.                             );
  1034.                         }
  1035.                         callback(response);
  1036.                     }
  1037.                );
  1038.            }
  1039.         },
  1040.         rev
  1041.     );
  1042. }
  1043.  
  1044. Syncd2SyncEngine.prototype.getrevision  = function(rev,callback) {
  1045.     var self = this;
  1046.     var funcFetched = function(response) {
  1047.         if (self._responseOK(response, callback)) {
  1048.             callback(0, response);
  1049.         }
  1050.     }
  1051.  
  1052.     //Xmarks.SetProgressMessage("progress.gettingprofilenames");
  1053.     var request = new Request("POST",
  1054.         { path: "/sync/bookmarks/state",
  1055.           host: Xmarks.gSettings.getServerHost(self._datasource.syncType) },
  1056.         this._args({rev: rev}) );
  1057.     request.Start(funcFetched);
  1058.  
  1059. }
  1060. Syncd2SyncEngine.prototype.getrevisions  = function(callback) {
  1061.     var self = this;
  1062.     var funcFetched = function(response) {
  1063.         if (self._responseOK(response, callback)) {
  1064.             callback(0, response);
  1065.         }
  1066.     }
  1067.  
  1068.     //Xmarks.SetProgressMessage("progress.gettingprofilenames");
  1069.     var request = new Request("POST",
  1070.         { path: "/sync/bookmarks/getrevisions",
  1071.           host: Xmarks.gSettings.getServerHost(self._datasource.syncType) },
  1072.         this._args({limit: Xmarks.gSettings.getRevisionLimit}) );
  1073.     request.Start(funcFetched);
  1074.  
  1075. }
  1076. Syncd2SyncEngine.prototype.verifypin = function(pin, callback) {
  1077.     var self = this;
  1078.     var ns;
  1079.     var revision;
  1080.  
  1081.     var funcDone = function(response) {
  1082.         if (self._responseOK(response, callback)) {
  1083.            Xmarks.LogWrite("Verifying PIN (Received Test Node)");
  1084.            if(!response.nodes.ROOT.data){
  1085.                 Xmarks.LogWrite("WARNING: Bad Root Problem");
  1086.                 self.verifypinbrokenroot(pin, callback);
  1087.            } else {
  1088.                 var result = self._datasource.verifyPin(pin, response.nodes.ROOT); 
  1089.                 callback(result ? 0 : 100);
  1090.            }
  1091.         }
  1092.     };
  1093.  
  1094.     //Xmarks.SetProgressMessage("progress.verifying");
  1095.     Xmarks.LogWrite("Verifying PIN (Starting)");
  1096.     self.request = new Request("POST", 
  1097.         {
  1098.             path: "/sync/" + self._datasource.syncType + "/state",
  1099.             host: Xmarks.gSettings.getServerHost(self._datasource.syncType)
  1100.         }, {"nodes":"ROOT", "depth":"self"});
  1101.     self.request.Start(funcDone);
  1102. }
  1103. // Fetch changes from the server. Passes to callback an object containing:
  1104. // * continous: true if the changes on the server are continuous.
  1105. // * mscs: the minimal server commandset. 
  1106. // * other server context info (in this case, toprev) to passed into
  1107. //   _writeServerChanges().
  1108.  
  1109.  
  1110. Syncd2SyncEngine.prototype._getServerChanges = function(callback) {
  1111.     // Get changes.
  1112.     var self = this;
  1113.     var toprev = 0;
  1114.     var sns = new Nodeset(self._datasource, self._baseline);
  1115.  
  1116.     var funcProcessResponse = function(response) {
  1117.         if (self._responseOK(response, callback)) {
  1118.             if (response.continuous == false) {
  1119.                 callback(0, { continuous: false });
  1120.             } else {
  1121.                 if (!response.toprev || !response.commands) {
  1122.                     Xmarks.LogWrite("Error: Invalid response to getchanges");
  1123.                     callback(500);
  1124.                     return;
  1125.                 }
  1126.                 toprev = response.toprev;
  1127.                 // Calculate mcs.
  1128.                 if (response.commands.length) {
  1129.                     scs = new Commandset(response.commands);
  1130.                     try {
  1131.                         scs.execute(sns);
  1132.                     } catch (e) {
  1133.                         Xmarks.LogWrite("execute failed: " + e.toSource());
  1134.                         if(typeof e == "number"){
  1135.                             callback(e);
  1136.                         } else {
  1137.                             callback(4);
  1138.                         }
  1139.                         return;
  1140.                     }
  1141.                     var base = new Nodeset(self._datasource,self._baseline);
  1142.                     base.Compare(sns, funcCalculatedMcs);
  1143.                 } else {
  1144.                     // Not modified
  1145.                     callback(0, { mscs: new Commandset(), toprev: toprev, 
  1146.                             sns: sns, continuous: true});
  1147.                 }
  1148.             }
  1149.         }
  1150.     };
  1151.     var funcCalculatedMcs = function(status, cs) {
  1152.         if (self._responseOK(status, callback)) {
  1153.             Xmarks.LogWrite(">>> Finished mcs: " + cs.toSource());
  1154.             callback(0, { mscs: cs, toprev: toprev, continuous: true, 
  1155.                     sns: sns });
  1156.         }
  1157.     };
  1158.  
  1159.     self.request = new Request("POST", 
  1160.         {
  1161.             path: "/sync/" + self._datasource.syncType + "/getchanges",
  1162.             host: Xmarks.gSettings.getServerHost(self._datasource.syncType)
  1163.         },
  1164.         self._args({ "baserev": self._baseline.currentRevision}) );
  1165.     self.request.Start(funcProcessResponse);
  1166. }
  1167.  
  1168. OwnSyncEngine.prototype.testDupURL = function(){
  1169.     // force check to see if user has two different urls for password
  1170.     // and bookmark
  1171.     if(Xmarks.gSettings.isSyncEnabled("bookmarks") && 
  1172.         Xmarks.gSettings.isSyncEnabled("passwords")){
  1173.         if(Xmarks.gSettings.getUrlWithUsernameAndPassword("passwords") ==
  1174.             Xmarks.gSettings.getUrlWithUsernameAndPassword("bookmarks")){
  1175.                 return true;
  1176.         }
  1177.     }
  1178.     return false;
  1179. }
  1180. OwnSyncEngine.prototype._getServerChanges = function(callback) {
  1181.     // Download the file, but only if the etag doesn't match
  1182.     var self = this;
  1183.     var headers = {};
  1184.     var token;
  1185.     var etag;
  1186.     var sns;
  1187.  
  1188.     var funcProcessFile = function(response) {
  1189.         if (response.status == 304) {   // Not modified.
  1190.             callback(0, { continuous: true, mscs: new Commandset(), 
  1191.                 token: Xmarks.gSettings.getToken(self._datasource.syncType), etag: response.etag });
  1192.             return;
  1193.         }
  1194.         if (self._responseOK(response, callback)) {
  1195.             if (response.token != Xmarks.gSettings.getToken(self._datasource.syncType)) {
  1196.                 callback(0, { continuous: false, etag: response.etag } );
  1197.                 return;
  1198.             }
  1199.  
  1200.             token = response.token;
  1201.             etag = response.etag;
  1202.             var cs = new Commandset(response.commands);
  1203.             sns = new Nodeset(self._datasource);
  1204.             try {
  1205.                 sns.Execute(cs);
  1206.             } catch (e) {
  1207.                 Xmarks.LogWrite("execute failed: " + e.toSource());
  1208.                 if(typeof e == "number"){
  1209.                     callback(e);
  1210.                 } else {
  1211.                     callback(4);
  1212.                 }
  1213.                 return;
  1214.             }
  1215.  
  1216.             var base = new Nodeset(self._datasource,self._baseline);
  1217.             base.Compare(sns, funcCompared);
  1218.         }
  1219.     };
  1220.     var funcCompared = function(status, cs) {
  1221.         if (self._responseOK(status)) {
  1222.             callback(0, { continuous: true, mscs: cs, token: token, 
  1223.                     sns: sns, etag: etag });
  1224.         }
  1225.     };
  1226.  
  1227.     var serveretag = Xmarks.gSettings.getEtag(this._datasource.syncType);
  1228.     if (serveretag.length) {
  1229.         headers["If-None-Match"] = serveretag;
  1230.     }
  1231.     self.request = new Request("GET", 
  1232.         Xmarks.gSettings.getUrlWithUsernameAndPassword(
  1233.             self._datasource.syncType
  1234.         ),
  1235.         null, 
  1236.         {
  1237.             isAuthRequest: false,
  1238.             headers: headers
  1239.         }
  1240.     );
  1241.     self.request.Start(funcProcessFile);
  1242. }
  1243.  
  1244. Syncd2SyncEngine.prototype._writeServerChanges = function(lcs, lns, sco, conflicts,
  1245.         callback) {
  1246.  
  1247.     var self = this;
  1248.     var funcWrote = function(response) {
  1249.         if (self._responseOK(response, callback)) {
  1250.             sco.toprev = response.toprev;
  1251.             if (!sco.toprev) {
  1252.                 Xmarks.LogWrite("Error: Invalid response to putchanges");
  1253.                 callback(500);
  1254.             } else {
  1255.                 callback(0);
  1256.             }
  1257.         }
  1258.     };
  1259.  
  1260.     self.request = new Request("POST", 
  1261.         {
  1262.             path: "/sync/" + self._datasource.syncType + "/putchanges",
  1263.             host: Xmarks.gSettings.getServerHost(self._datasource.syncType)
  1264.         },
  1265.         self._args({ baserev : sco.toprev, commands : lcs, log : conflicts } ));
  1266.     self.request.Start(funcWrote);
  1267. }
  1268.  
  1269. OwnSyncEngine.prototype._writeServerChanges = function(lcs, lns, sco, conflicts,
  1270.         callback) {
  1271.     var self = this;
  1272.  
  1273.     var funcGotCommandset = function(status, cs) {
  1274.         var header = {};
  1275.         if (!Xmarks.gSettings.disableIfMatchOnPut && sco.etag && sco.etag.length) {
  1276.             header = { "Etag" : sco.etag };
  1277.         }
  1278.         if (self._responseOK(status, callback)) {
  1279.             self.request = new Request("PUT", 
  1280.                 Xmarks.gSettings.getUrlWithUsernameAndPassword(self._datasource.syncType),
  1281.                 { token: sco.token, commands: cs },
  1282.                 {
  1283.                     isAuthRequest: false,
  1284.                     headers: header,
  1285.                     ignoreBody: true
  1286.                 }
  1287.             );
  1288.             self.request.Start(funcWritten);
  1289.         }
  1290.     };
  1291.     var funcWritten = function(response) {
  1292.         if (self._responseOK(response, callback)) {
  1293.             sco.etag = response.etag;
  1294.             callback(0);
  1295.         }
  1296.     };
  1297.  
  1298.     lns.ProvideCommandset(funcGotCommandset);
  1299. }
  1300.  
  1301. Syncd2SyncEngine.prototype.status = function(callback) {
  1302.     var self = this;
  1303.  
  1304.     var funcCheckStatus = function(response) {
  1305.         if (self._responseOK(response, callback)) {
  1306.             if (!response.toprev) {
  1307.                 Xmarks.LogWrite("Error: Invalid response to status");
  1308.                 callback(500);
  1309.                 return;
  1310.             }
  1311.             callback(0, response);
  1312.         }
  1313.     };
  1314.  
  1315.     Xmarks.LogWrite("Entered Status...");
  1316.     //Xmarks.SetProgressMessage("progress.verifying");
  1317.  
  1318.     self.request = new Request("POST",
  1319.         { path: "/sync/" + self._datasource.syncType + "/status",
  1320.           host: Xmarks.gSettings.getServerHost(self._datasource.syncType)
  1321.         }, {});
  1322.     self.request.Start(funcCheckStatus);
  1323. }
  1324. Syncd2SyncEngine.prototype.extstatus = function(callback) {
  1325.     var self = this;
  1326.     this.status(function(normal_status,response){
  1327.         // if we get an error, do callback right away
  1328.         if(normal_status){
  1329.             callback(normal_status, response);
  1330.  
  1331.         // if we are reset, no need to check if we are purged
  1332.         } else if(response.isreset){
  1333.             response.ispurged = false;
  1334.             callback(normal_status, response);
  1335.         // call for the root to see if we get a 444
  1336.         } else {
  1337.             self.request = new Request("POST", 
  1338.                 {
  1339.                     path: "/sync/" + self._datasource.syncType + "/state",
  1340.                     host: Xmarks.gSettings.getServerHost(self._datasource.syncType)
  1341.                 }, {"nodes":"ROOT", "depth":"self"});
  1342.             self.request.Start(function(stateresponse){
  1343.                 if (self._responseOK(stateresponse, function(errnum){
  1344.                     if(errnum == 444){
  1345.                         response.ispurged = true;
  1346.                         callback(0, response);
  1347.                     } else {
  1348.                         callback(errnum, response);
  1349.                     }
  1350.                 })) {
  1351.                     response.ispurged = false;
  1352.                     callback(0, response);
  1353.                 }
  1354.             });
  1355.         }
  1356.     });
  1357.  
  1358. }
  1359. OwnSyncEngine.prototype.status = function(callback) {
  1360.     var self = this;
  1361.  
  1362.     var funcHandleResponse = function(response) {
  1363.         if (response.status == 404) {
  1364.             callback(0, { status: 0, isreset: true, ispurged: false });
  1365.         } else if (self._responseOK(response, callback)) {
  1366.             callback(0, { status: 0, isreset: false, ispurged: false });
  1367.         }
  1368.     };
  1369.  
  1370.     Xmarks.LogWrite("Entered status...");
  1371.     if(this.testDupURL()){
  1372.         callback(1011);
  1373.         return;
  1374.     }
  1375.     //Xmarks.SetProgressMessage("progress.verifying");
  1376.     self.request = new Request("GET",
  1377.         Xmarks.gSettings.getUrlWithUsernameAndPassword(self._datasource.syncType));
  1378.     self.request.Start(funcHandleResponse);
  1379. }
  1380. OwnSyncEngine.prototype.extstatus = OwnSyncEngine.prototype.status;
  1381.  
  1382.  
  1383. Syncd2SyncEngine.prototype._getServerState = function(callback) {
  1384.     var self = this;
  1385.     self.request = new Request("POST", 
  1386.         {
  1387.             path: "/sync/" + self._datasource.syncType +  "/download",
  1388.             host: Xmarks.gSettings.getServerHost(self._datasource.syncType)
  1389.         }, self._args({}) );
  1390.     self.request.Start(callback);
  1391. }
  1392.  
  1393. OwnSyncEngine.prototype._getServerState = function(callback) {
  1394.     var self = this;
  1395.     self.request = new Request("GET",
  1396.         Xmarks.gSettings.getUrlWithUsernameAndPassword(self._datasource.syncType));
  1397.     self.request.Start(callback);
  1398. }
  1399.  
  1400. SyncEngine.prototype.merge = function(local, callback) {
  1401.     // Perform a merge (an "additive sync").
  1402.     // local is a boolean which determines what merge uses
  1403.     // as the starting set: true for local, false for server.
  1404.  
  1405.     Xmarks.LogWrite("Entered Merge...");
  1406.  
  1407.     var self = this;
  1408.     var serverNS = new Nodeset(self._datasource);
  1409.     var localNS = new Nodeset(self._datasource);
  1410.     var mergedNS = null;
  1411.     var revision;
  1412.     var sco;
  1413.  
  1414.     // resetPIN trumps merge
  1415.     if(Xmarks.gSettings.mustUpload(self._datasource.syncType)){
  1416.         Xmarks.LogWrite("Forced Upload: Pin Reset");
  1417.         self.upload(function(response){
  1418.             if(self._responseOK(response, callback)){
  1419.                 Xmarks.gSettings.setMustUpload(self._datasource.syncType, false);
  1420.                 callback(response);
  1421.             }
  1422.         });
  1423.         return;
  1424.     }
  1425.  
  1426.     var funcDone = function(response) {
  1427.         if (self._responseOK(response, callback)) {
  1428.             sco = response;
  1429.             var cs = new Commandset(response.commands);
  1430.             try {
  1431.                 cs.execute(serverNS);
  1432.             } catch (e) {
  1433.                 if(typeof e == "number"){
  1434.                     callback(e);
  1435.                 } else {
  1436.                     Xmarks.LogWrite("execute failed: " + e.toSource());
  1437.                     callback(4);
  1438.                 }
  1439.                 return;
  1440.             }
  1441.             localNS.FetchFromNative(funcFetched);
  1442.         }
  1443.     };
  1444.     var funcFetched = function( e ) {
  1445.  
  1446.         if (e) {
  1447.             callback(e);
  1448.             return;
  1449.         }
  1450.         //Xmarks.SetProgressMessage("progress.merging");
  1451.         if (local) {
  1452.             localNS.Merge(serverNS);
  1453.             mergedNS = localNS;
  1454.         } else {
  1455.             mergedNS = new Nodeset(self._datasource, serverNS);
  1456.             mergedNS.Merge(localNS);
  1457.         }
  1458.  
  1459.         // calculate server's MCS
  1460.         Xmarks.LogWrite("Finished merge; calculating mcs");
  1461.         serverNS.Compare(mergedNS, funcWriteChanges);
  1462.     };
  1463.     var funcWriteChanges = function(status, cs) {
  1464.         if (self._responseOK(status, callback)) {
  1465.             // Write changes to the server.
  1466.             //Xmarks.SetProgressMessage("progress.writing");
  1467.             if (cs.set.length > 0) {
  1468.                 self._writeServerChanges(cs, mergedNS, sco, null, 
  1469.                     funcWroteServerChanges);
  1470.             } else {
  1471.                 funcWroteServerChanges(0);
  1472.             }
  1473.         }
  1474.     };
  1475.     var funcWroteServerChanges = function(response) {
  1476.         // Write 'em back to native store, too
  1477.         if (self._responseOK(response, callback)) {
  1478.             //Xmarks.SetProgressMessage("progress.loading");
  1479.             mergedNS.FlushToNative(funcFlushed);
  1480.         }
  1481.     };
  1482.     var funcFlushed = function(response) {
  1483.         if (self._responseOK(response, callback)) {
  1484.             Xmarks.gSettings.setMustMerge(self._datasource.syncType, false);
  1485.             self._completeTransaction(mergedNS, sco, callback);
  1486.         }
  1487.     };
  1488.  
  1489.     //Xmarks.SetProgressMessage("progress.downloading");
  1490.     self._getServerState(funcDone);
  1491. }
  1492.  
  1493. Syncd2SyncEngine.prototype._fetchBaseline = function(callback) {
  1494.     // Fetch the baseline if necessary
  1495.     var self = this;
  1496.  
  1497.     var funcLoadedFromServer = function(response) {
  1498.         if (self._responseOK(response, callback)) {
  1499.             var cs = new Commandset(response.commands);
  1500.             try {
  1501.                 cs.execute(self._baseline);
  1502.             } catch (e) {
  1503.                 Xmarks.LogWrite("execute failed: " + e.toSource());
  1504.                 if(typeof e == "number"){
  1505.                     callback(e);
  1506.                 } else {
  1507.                     callback(4);
  1508.                 }
  1509.                 return;
  1510.             }
  1511.             self._baseline.currentRevision = response.rev;
  1512.             self._baseline.SaveToFile(funcLoaded);
  1513.             return;
  1514.         } else {
  1515.             Xmarks.LogWrite("Failed to load baseline from server.");
  1516.         }
  1517.     };
  1518.     var funcLoaded = function() {
  1519.         // Loaded from file or server
  1520.         // var nat = new NativeDatasource();
  1521.         self._baseline.BaselineLoaded(self._baseline, funcGotIt);
  1522.         return;
  1523.     };
  1524.     var funcGotIt = function(status) {
  1525.         if (self._responseOK(status, callback)) {
  1526.             self._baseline.hash = Xmarks.gSettings.hash;
  1527.             callback(0);
  1528.         }
  1529.     };
  1530.  
  1531.     if (self._baseline && self._baseline.hash == Xmarks.gSettings.hash) {
  1532.         Xmarks.LogWrite("Using Baseline Cache.");
  1533.         funcGotIt(0);
  1534.         return;
  1535.     }
  1536.  
  1537.     self._baseline = new Nodeset(self._datasource);
  1538.     try {
  1539.         self._baseline.LoadFromFile();
  1540.     } catch (e) {
  1541.         // We failed to load our baseline locally -- try getting
  1542.         // it from the server.
  1543.         Xmarks.LogWrite("Failed to load baseline from file: " + e.name);
  1544.         var request = new Request("POST",
  1545.             {
  1546.                 path: "/sync/" + self._datasource.syncType + "/download",
  1547.                 host: Xmarks.gSettings.getServerHost(self._datasource.syncType)
  1548.             },
  1549.             self._args({ rev: Xmarks.gSettings.GetSyncRevision(self._datasource.syncType), depth: "all", 
  1550.                     log: { "error": e.name }}));
  1551.         request.Start(funcLoadedFromServer);
  1552.         return;
  1553.     }
  1554.  
  1555.     funcLoaded();
  1556. }
  1557.  
  1558. OwnSyncEngine.prototype._fetchBaseline = function(callback) {
  1559.     // Fetch the baseline if necessary
  1560.     var self = this;
  1561.  
  1562.     var funcLoaded = function() {
  1563.         // Loaded from file 
  1564.         // var nat = new NativeDatasource();
  1565.         self._baseline.BaselineLoaded(self._baseline, funcGotIt);
  1566.     };
  1567.     var funcGotIt = function(status) {
  1568.         if (self._responseOK(status, callback)) {
  1569.             self._baseline.hash = Xmarks.gSettings.hash;
  1570.             callback(0);
  1571.         }
  1572.     };
  1573.  
  1574.     if (self._baseline && self._baseline.hash == Xmarks.gSettings.hash) {
  1575.         funcGotIt(0);
  1576.         return;
  1577.     }
  1578.  
  1579.     self._baseline = new Nodeset(self._datasource);
  1580.     try {
  1581.         self._baseline.LoadFromFile();
  1582.     } catch (e) {
  1583.         // We failed to load our baseline locally -- we're hosed.
  1584.         Xmarks.LogWrite("Failed to load baseline.");
  1585.         if(typeof e == "number"){
  1586.             callback(e);
  1587.         } else {
  1588.             callback(4);
  1589.         }
  1590.         return;
  1591.     }
  1592.  
  1593.     funcLoaded();
  1594. }
  1595.  
  1596. Syncd2SyncEngine.prototype._completeTransaction = function(ns, sco, callback) {
  1597.     var self = this;
  1598.  
  1599.     var funcFinishUp = function(status) {
  1600.         if (self._responseOK(status, callback)) {
  1601.             self._baseline = ns;
  1602.             self._baseline.hash = Xmarks.gSettings.hash;
  1603.             self._baseline.currentRevision = sco.toprev;
  1604.             self._baseline.SaveToFile(funcFinishedWrite);
  1605.         }
  1606.     };
  1607.     var funcFinishedWrite = function(status) {
  1608.         if (self._responseOK(status, callback)) {
  1609.             // Xmarks.gSettings.currentRevision = sco.toprev;
  1610.             Xmarks.gSettings.SetSyncRevision(self._datasource.syncType, sco.toprev);
  1611.             callback(0);
  1612.         }
  1613.     };
  1614.  
  1615.     ns.Declone(funcFinishUp);
  1616. }
  1617.  
  1618. Syncd2SyncEngine.prototype.getProfileNames = function(callback) {
  1619.     var self = this;
  1620.     var funcFetched = function(response) {
  1621.         if (self._responseOK(response, callback)) {
  1622.             callback(0, response);
  1623.         }
  1624.     }
  1625.  
  1626.     //Xmarks.SetProgressMessage("progress.gettingprofilenames");
  1627.     var request = new Request("POST",
  1628.         { path: "/user/profiles/getnames",
  1629.           host: Xmarks.gSettings.acctMgrHost },
  1630.         this._args({}) );
  1631.     request.Start(funcFetched);
  1632.  
  1633. }
  1634.  
  1635. OwnSyncEngine.prototype.upload = function(callback) {
  1636.     var self = this;
  1637.     var ns = new Nodeset(self._datasource);
  1638.     var token = Date.now().toString(16);
  1639.  
  1640.     // if we are doing a force upload, then that trumps forcedMerge
  1641.     if(Xmarks.gSettings.mustUpload(self._datasource.syncType)){
  1642.         Xmarks.gSettings.setMustMerge(self._datasource.syncType, false);
  1643.     }
  1644.     // even though the user says upload, we don't want
  1645.     // to do that for passwords during initial sync
  1646.     if(Xmarks.gSettings.mustMerge(self._datasource.syncType)){
  1647.         Xmarks.LogWrite("Forced Merge for Passwords");
  1648.         self.merge(true, function(response){
  1649.             if(self._responseOK(response, callback)){
  1650.                 Xmarks.gSettings.setMustMerge(self._datasource.syncType, false);
  1651.                 callback(response);
  1652.             }
  1653.         });
  1654.         return;
  1655.     }
  1656.  
  1657.  
  1658.     var funcFetched = function(status) {
  1659.         if (self._responseOK(status, callback)) {
  1660.             ns.ProvideCommandset(funcContinue);
  1661.         }
  1662.     };
  1663.     var funcContinue = function(status, cs) {
  1664.         if (self._responseOK(status, callback)) {
  1665.             self.request = new Request("PUT", 
  1666.                 Xmarks.gSettings.getUrlWithUsernameAndPassword(self._datasource.syncType),
  1667.                 { commands: cs, token: token }, 
  1668.                 { ignoreBody: true }
  1669.             );
  1670.             self.request.Start(funcDone);
  1671.         }
  1672.     };
  1673.     var funcDone = function(response) {
  1674.         if (self._responseOK(response, callback)) {
  1675.             Xmarks.gSettings.setMustUpload(self._datasource.syncType, false);
  1676.             self._completeTransaction(ns, 
  1677.                 { etag: response.etag, token: token }, callback);
  1678.         }
  1679.     };
  1680.  
  1681.     //Xmarks.SetProgressMessage("progress.writing");
  1682.     ns.FetchFromNative(funcFetched);
  1683. }
  1684.  
  1685. OwnSyncEngine.prototype.verifypin = function(pin, callback) {
  1686.     var self = this;
  1687.     var ns;
  1688.  
  1689.     var funcDone = function(response) {
  1690.         var funcFinished = function(resp) {
  1691.             if (self._responseOK(resp, callback)) {
  1692.                 self._completeTransaction(ns, 
  1693.                     { etag: response.etag, token: response.token }, callback);
  1694.             }
  1695.         };
  1696.         if (self._responseOK(response, callback)) {
  1697.             ns = new Nodeset(self._datasource);
  1698.             var cs = new Commandset(response.commands);
  1699.             try {
  1700.                 cs.execute(ns);
  1701.             } catch (e) {
  1702.                 Xmarks.LogWrite("execute failed: " + e.toSource());
  1703.                 if(typeof e == "number"){
  1704.                     callback(e);
  1705.                 } else {
  1706.                     callback(4);
  1707.                 }
  1708.                 return;
  1709.             }
  1710.  
  1711.            var result = self._datasource.verifyPin(pin, ns.Node(NODE_ROOT)); 
  1712.            callback(result ? 0 : 100);
  1713.         }
  1714.     };
  1715.     //Xmarks.SetProgressMessage("progress.verifying");
  1716.     Xmarks.LogWrite("Verifying PIN (Own Server)");
  1717.     self.request = new Request("GET", Xmarks.gSettings.getUrlWithUsernameAndPassword(self._datasource.syncType));
  1718.     self.request.Start(funcDone);
  1719. }
  1720.  
  1721. OwnSyncEngine.prototype.download = function(callback) {
  1722.     var self = this;
  1723.     var ns;
  1724.  
  1725.     // resetPIN trumps download (only occurs in the case of
  1726.     // resetPIN in setup dialog)
  1727.     if(Xmarks.gSettings.mustUpload(self._datasource.syncType)){
  1728.         Xmarks.LogWrite("Forced Upload: Pin Reset");
  1729.         self.upload(function(response){
  1730.             if(self._responseOK(response, callback)){
  1731.                 Xmarks.gSettings.setMustUpload(self._datasource.syncType, false);
  1732.                 callback(response);
  1733.             }
  1734.         });
  1735.         return;
  1736.     }
  1737.     else if(Xmarks.gSettings.mustMerge(self._datasource.syncType)){
  1738.         Xmarks.LogWrite("Forced Merge for Passwords");
  1739.         self.merge(true, function(response){
  1740.             if(self._responseOK(response, callback)){
  1741.                 Xmarks.gSettings.setMustMerge(self._datasource.syncType, false);
  1742.                 callback(response);
  1743.             }
  1744.         });
  1745.         return;
  1746.     }
  1747.  
  1748.     var funcDone = function(response) {
  1749.         var funcFinished = function(resp) {
  1750.             if (self._responseOK(resp, callback)) {
  1751.                 self._completeTransaction(ns, 
  1752.                     { etag: response.etag, token: response.token }, callback);
  1753.             }
  1754.         };
  1755.         if (self._responseOK(response, callback)) {
  1756.             ns = new Nodeset(self._datasource);
  1757.             var cs = new Commandset(response.commands);
  1758.             try {
  1759.                 cs.execute(ns);
  1760.             } catch (e) {
  1761.                 Xmarks.LogWrite("execute failed: " + e.toSource());
  1762.                 if(typeof e == "number"){
  1763.                     callback(e);
  1764.                 } else {
  1765.                     callback(4);
  1766.                 }
  1767.                 return;
  1768.             }
  1769.             //Xmarks.SetProgressMessage("progress.loading");
  1770.             ns.FlushToNative(funcFinished);
  1771.         }
  1772.     };
  1773.  
  1774.     //Xmarks.SetProgressMessage("progress.downloading");
  1775.     self.request = new Request("GET", Xmarks.gSettings.getUrlWithUsernameAndPassword(self._datasource.syncType));
  1776.     self.request.Start(funcDone);
  1777. }
  1778.  
  1779. OwnSyncEngine.prototype._completeTransaction = 
  1780.         function(ns, sco, callback) {
  1781.     var self = this;
  1782.  
  1783.     var funcFinishUp = function(status) {
  1784.         if (self._responseOK(status, callback)) {
  1785.             self._baseline = ns;
  1786.             self._baseline.hash = Xmarks.gSettings.hash;
  1787.             self._baseline.SaveToFile(funcFinishedWrite);
  1788.         }
  1789.     };
  1790.     var funcFinishedWrite = function(status) {
  1791.         if (self._responseOK(status, callback)) {
  1792.             Xmarks.gSettings.setToken(self._datasource.syncType, sco.token);
  1793.             if (sco.etag) Xmarks.gSettings.setEtag(self._datasource.syncType, sco.etag);
  1794.             Xmarks.gSettings.SyncComplete(self._datasource.syncType);
  1795.             callback(0);
  1796.         }
  1797.     };
  1798.     ns.Declone(funcFinishUp);
  1799. }
  1800.  
  1801.  
  1802.